package com.tonyodev.fetch2.fetch;

import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;

import com.tonyodev.fetch2.Download;
import com.tonyodev.fetch2.Error;
import com.tonyodev.fetch2.FetchGroup;
import com.tonyodev.fetch2.FetchGroupListener;
import com.tonyodev.fetch2.FetchListener;
import com.tonyodev.fetch2.FetchNotificationManager;
import com.tonyodev.fetch2.database.DownloadInfo;
import com.tonyodev.fetch2.provider.DownloadProvider;
import com.tonyodev.fetch2.provider.GroupInfoProvider;
import com.tonyodev.fetch2core.DownloadBlock;
import com.tonyodev.fetch2core.FetchObserver;
import com.tonyodev.fetch2core.Reason;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

import timber.log.Timber;

/**
 * @since 2021-05-20
 */
public class ListenerCoordinator {

    public String namespace;
    private GroupInfoProvider groupInfoProvider;
    private DownloadProvider downloadProvider;
    private EventHandler uiHandler;
    private Object lock;
    private HashMap<Integer, ArrayList<WeakReference<FetchListener>>> fetchListenerMap = new HashMap();
    private HashMap<Integer, ArrayList<WeakReference<FetchGroupListener>>> fetchGroupListenerMap = new HashMap();
    private ArrayList<FetchNotificationManager> fetchNotificationManagerList = new ArrayList<>();
    private MyEventHandler fetchNotificationHandler = fetchNotificationHandler();
    private HashMap<Integer, List<WeakReference<FetchObserver<Download>>>> downloadsObserverMap = new HashMap<>();

    public ListenerCoordinator(String namespace, GroupInfoProvider groupInfoProvider, DownloadProvider downloadProvider, EventHandler uiHandler) {
        this.namespace = namespace;
        this.groupInfoProvider = groupInfoProvider;
        this.downloadProvider = downloadProvider;
        this.uiHandler = uiHandler;
        this.lock = new Object();
    }

    private MyEventHandler fetchNotificationHandler() {
        EventRunner current = EventRunner.current();
        MyEventHandler myEventHandler = null;
        if (current != null) {
            myEventHandler = new MyEventHandler(current);
        }
        return myEventHandler;
    }

    public FetchListener getMainListener() {
        return this.mainListener;
    }

    public void addListener(int id, FetchListener fetchListener) {
        synchronized (lock) {
            ArrayList<WeakReference<FetchListener>> set = fetchListenerMap.get(id);
            if(set == null){
                set = new ArrayList<>();
            }
            set.add(new WeakReference(fetchListener));
            fetchListenerMap.put(id, set);
            if (fetchListener instanceof FetchGroupListener) {
                ArrayList<WeakReference<FetchGroupListener>> groupSet = fetchGroupListenerMap.get(id);
                if(groupSet == null){
                    groupSet = new ArrayList<>();
                }
                groupSet.add(new WeakReference(fetchListener));
                fetchGroupListenerMap.put(id, groupSet);
            }
        }
    }

    public void removeListener(int id, FetchListener fetchListener) {
        synchronized (lock) {
            ArrayList<WeakReference<FetchListener>> weakReferences = fetchListenerMap.get(id);
            if (weakReferences != null) {
                Iterator<WeakReference<FetchListener>> iterator = weakReferences.iterator();
                if (iterator != null) {
                    while (iterator.hasNext()) {
                        WeakReference<FetchListener> reference = iterator.next();
                        if (reference.get() == fetchListener) {
                            iterator.remove();
                            break;
                        }
                    }
                }
            }

            if (fetchListener instanceof FetchGroupListener && fetchGroupListenerMap.get(id) != null) {
                Iterator<WeakReference<FetchGroupListener>> groupIterator = fetchGroupListenerMap.get(id).iterator();
                if (groupIterator != null) {
                    while (groupIterator.hasNext()) {
                        WeakReference<FetchGroupListener> reference = groupIterator.next();
                        if (reference.get() == fetchListener) {
                            groupIterator.remove();
                            break;
                        }
                    }
                }
            }
        }
    }

    public void addNotificationManager(FetchNotificationManager fetchNotificationManager) {
        synchronized (lock) {
            if (!fetchNotificationManagerList.contains(fetchNotificationManager)) {
                fetchNotificationManagerList.add(fetchNotificationManager);
            }
        }
    }

    public void removeNotificationManager(FetchNotificationManager fetchNotificationManager) {
        synchronized (lock) {
            fetchNotificationManagerList.remove(fetchNotificationManager);
        }
    }

    public void cancelOnGoingNotifications(FetchNotificationManager fetchNotificationManager) {
        synchronized (lock) {
            fetchNotificationHandler.postTask(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock) {
                        fetchNotificationManager.cancelOngoingNotifications();
                    }
                }
            });
        }
    }

    FetchListener mainListener = new FetchListener() {
        @Override
        public void onAdded(Download download) {
            Timber.d("onAdded...");
            synchronized (lock) {
                fetchListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchListener>>>() {
                    @Override
                    public void accept(ArrayList<WeakReference<FetchListener>> weakReferences) {
                        Iterator<WeakReference<FetchListener>> iterator = weakReferences.iterator();
                        while (iterator.hasNext()) {
                            FetchListener fetchListener = iterator.next().get();
                            if (fetchListener == null) {
                                iterator.remove();
                            } else {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        fetchListener.onAdded(download);
                                    }
                                });
                            }
                        }
                    }
                });
                if (fetchGroupListenerMap != null && !fetchGroupListenerMap.isEmpty()) {
                    int groupId = download.getGroup();
                    FetchGroup fetchGroup = groupInfoProvider.getGroupReplace(groupId, (DownloadInfo) download, Reason.DOWNLOAD_ADDED);
                    fetchGroupListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchGroupListener>>>() {
                        @Override
                        public void accept(ArrayList<WeakReference<FetchGroupListener>> weakReferences) {
                            Iterator<WeakReference<FetchGroupListener>> iterator = weakReferences.iterator();
                            while (iterator.hasNext()) {
                                FetchGroupListener fetchListener = iterator.next().get();
                                if (fetchListener == null) {
                                    iterator.remove();
                                } else {
                                    uiHandler.postTask(new Runnable() {
                                        @Override
                                        public void run() {
                                            fetchListener.onAdded(groupId, download, fetchGroup);
                                        }
                                    });
                                }
                            }
                        }
                    });
                } else {
                    groupInfoProvider.postGroupReplace(download.getGroup(), (DownloadInfo) download, Reason.DOWNLOAD_ADDED);
                }
                List<WeakReference<FetchObserver<Download>>> downloadObserverSet = downloadsObserverMap.get(download.getId());
                if (downloadObserverSet != null) {
                    downloadObserverSet.forEach(new Consumer<WeakReference<FetchObserver<Download>>>() {
                        @Override
                        public void accept(WeakReference<FetchObserver<Download>> downloadWeakReference) {
                            FetchObserver<Download> observer = downloadWeakReference.get();
                            if (observer != null) {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        observer.onChanged(download, Reason.DOWNLOAD_ADDED);
                                    }
                                });
                            }
                        }
                    });
                }
            }
        }

        @Override
        public void onQueued(Download download, boolean waitingOnNetwork) {
            Timber.d("onQueued...");
            synchronized (lock) {
                fetchListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchListener>>>() {
                    @Override
                    public void accept(ArrayList<WeakReference<FetchListener>> weakReferences) {
                        Iterator<WeakReference<FetchListener>> iterator = weakReferences.iterator();
                        while (iterator.hasNext()) {
                            FetchListener fetchListener = iterator.next().get();
                            if (fetchListener == null) {
                                iterator.remove();
                            } else {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        fetchListener.onQueued(download, waitingOnNetwork);
                                    }
                                });
                            }
                        }
                    }
                });
                if (fetchGroupListenerMap != null && !fetchGroupListenerMap.isEmpty()) {
                    int groupId = download.getGroup();
                    FetchGroup fetchGroup = groupInfoProvider.getGroupReplace(groupId, (DownloadInfo) download, Reason.DOWNLOAD_QUEUED);
                    fetchGroupListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchGroupListener>>>() {
                        @Override
                        public void accept(ArrayList<WeakReference<FetchGroupListener>> weakReferences) {
                            Iterator<WeakReference<FetchGroupListener>> iterator = weakReferences.iterator();
                            while (iterator.hasNext()) {
                                FetchGroupListener fetchListener = iterator.next().get();
                                if (fetchListener == null) {
                                    iterator.remove();
                                } else {
                                    fetchListener.onQueued(groupId, download, waitingOnNetwork, fetchGroup);
                                }
                            }
                        }
                    });
                } else {
                    groupInfoProvider.postGroupReplace(download.getGroup(), (DownloadInfo) download, Reason.DOWNLOAD_QUEUED);
                }
                List<WeakReference<FetchObserver<Download>>> downloadObserverSet = downloadsObserverMap.get(download.getId());
                if (downloadObserverSet != null) {
                    downloadObserverSet.forEach(new Consumer<WeakReference<FetchObserver<Download>>>() {
                        @Override
                        public void accept(WeakReference<FetchObserver<Download>> fetchObserverWeakReference) {
                            FetchObserver<Download> observer = fetchObserverWeakReference.get();
                            if (observer != null) {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        observer.onChanged(download, Reason.DOWNLOAD_QUEUED);
                                    }
                                });
                            }
                        }
                    });
                }
            }
        }

        @Override
        public void onWaitingNetwork(Download download) {
            Timber.d("onWaitingNetwork...");
            synchronized (lock) {
                fetchListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchListener>>>() {
                    @Override
                    public void accept(ArrayList<WeakReference<FetchListener>> weakReferences) {
                        Iterator<WeakReference<FetchListener>> iterator = weakReferences.iterator();
                        while (iterator.hasNext()) {
                            FetchListener fetchListener = iterator.next().get();
                            if (fetchListener == null) {
                                iterator.remove();
                            } else {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        fetchListener.onWaitingNetwork(download);
                                    }
                                });
                            }
                        }
                    }
                });
                if (fetchGroupListenerMap != null && !fetchGroupListenerMap.isEmpty()) {
                    int groupId = download.getGroup();
                    FetchGroup fetchGroup = groupInfoProvider.getGroupReplace(groupId, (DownloadInfo) download, Reason.DOWNLOAD_WAITING_ON_NETWORK);
                    fetchGroupListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchGroupListener>>>() {
                        @Override
                        public void accept(ArrayList<WeakReference<FetchGroupListener>> weakReferences) {
                            Iterator<WeakReference<FetchGroupListener>> iterator = weakReferences.iterator();
                            while (iterator.hasNext()) {
                                FetchGroupListener fetchListener = iterator.next().get();
                                if (fetchListener == null) {
                                    iterator.remove();
                                } else {
                                    fetchListener.onWaitingNetwork(groupId, download, fetchGroup);
                                }
                            }
                        }
                    });
                } else {
                    groupInfoProvider.postGroupReplace(download.getGroup(), (DownloadInfo) download, Reason.DOWNLOAD_WAITING_ON_NETWORK);
                }
                List<WeakReference<FetchObserver<Download>>> downloadObserverSet = downloadsObserverMap.get(download.getId());
                if (downloadObserverSet != null) {
                    downloadObserverSet.forEach(new Consumer<WeakReference<FetchObserver<Download>>>() {
                        @Override
                        public void accept(WeakReference<FetchObserver<Download>> fetchObserverWeakReference) {
                            FetchObserver<Download> observer = fetchObserverWeakReference.get();
                            if (observer != null) {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        observer.onChanged(download, Reason.DOWNLOAD_WAITING_ON_NETWORK);
                                    }
                                });
                            }
                        }
                    });
                }
            }
        }

        @Override
        public void onCompleted(Download download) {
            Timber.d("onCompleted...");
            synchronized (lock) {
                fetchNotificationHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        synchronized (lock) {
                            for (FetchNotificationManager fetchNotificationManager : fetchNotificationManagerList) {
                                if (fetchNotificationManager.postDownloadUpdate(download)) break;
                            }
                        }
                    }
                });
                fetchListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchListener>>>() {
                    @Override
                    public void accept(ArrayList<WeakReference<FetchListener>> weakReferences) {
                        Iterator<WeakReference<FetchListener>> iterator = weakReferences.iterator();
                        while (iterator.hasNext()) {
                            FetchListener fetchListener = iterator.next().get();
                            if (fetchListener == null) {
                                iterator.remove();
                            } else {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        fetchListener.onCompleted(download);
                                    }
                                });
                            }
                        }
                    }
                });
                if (fetchGroupListenerMap != null && !fetchGroupListenerMap.isEmpty()) {
                    int groupId = download.getId();
                    FetchGroup fetchGroup = groupInfoProvider.getGroupReplace(groupId, (DownloadInfo) download, Reason.DOWNLOAD_COMPLETED);
                    fetchGroupListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchGroupListener>>>() {
                        @Override
                        public void accept(ArrayList<WeakReference<FetchGroupListener>> weakReferences) {
                            Iterator<WeakReference<FetchGroupListener>> iterator = weakReferences.iterator();
                            while (iterator.hasNext()) {
                                FetchGroupListener fetchListener = iterator.next().get();
                                if (fetchListener == null) {
                                    iterator.remove();
                                } else {
                                    fetchListener.onCompleted(groupId, download, fetchGroup);
                                }
                            }
                        }
                    });
                } else {
                    groupInfoProvider.postGroupReplace(download.getGroup(), (DownloadInfo) download, Reason.DOWNLOAD_COMPLETED);
                }
                List<WeakReference<FetchObserver<Download>>> downloadObserverSet = downloadsObserverMap.get(download.getId());
                if (downloadObserverSet != null) {
                    downloadObserverSet.forEach(new Consumer<WeakReference<FetchObserver<Download>>>() {
                        @Override
                        public void accept(WeakReference<FetchObserver<Download>> fetchObserverWeakReference) {
                            FetchObserver<Download> observer = fetchObserverWeakReference.get();
                            if (observer != null) {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        observer.onChanged(download, Reason.DOWNLOAD_COMPLETED);
                                    }
                                });
                            }
                        }
                    });
                }
            }
        }

        @Override
        public void onError(Download download, Error error, Throwable throwable) {
            Timber.d("onError...");
            synchronized (lock) {
                fetchNotificationHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        synchronized (lock) {
                            for (FetchNotificationManager fetchNotificationManager : fetchNotificationManagerList) {
                                if (fetchNotificationManager.postDownloadUpdate(download)) break;
                            }
                        }
                    }
                });
                fetchListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchListener>>>() {
                    @Override
                    public void accept(ArrayList<WeakReference<FetchListener>> weakReferences) {
                        Iterator<WeakReference<FetchListener>> iterator = weakReferences.iterator();
                        while (iterator.hasNext()) {
                            FetchListener fetchListener = iterator.next().get();
                            if (fetchListener == null) {
                                iterator.remove();
                            } else {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        fetchListener.onError(download, error, throwable);
                                    }
                                });
                            }
                        }
                    }
                });
                if (fetchGroupListenerMap != null && !fetchGroupListenerMap.isEmpty()) {
                    int groupId = download.getGroup();
                    FetchGroup fetchGroup = groupInfoProvider.getGroupReplace(groupId, (DownloadInfo) download, Reason.DOWNLOAD_ERROR);
                    fetchGroupListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchGroupListener>>>() {
                        @Override
                        public void accept(ArrayList<WeakReference<FetchGroupListener>> weakReferences) {
                            Iterator<WeakReference<FetchGroupListener>> iterator = weakReferences.iterator();
                            while (iterator.hasNext()) {
                                FetchGroupListener fetchListener = iterator.next().get();
                                if (fetchListener == null) {
                                    iterator.remove();
                                } else {
                                    fetchListener.onError(groupId, download, error, throwable, fetchGroup);
                                }
                            }
                        }
                    });
                } else {
                    groupInfoProvider.postGroupReplace(download.getGroup(), (DownloadInfo) download, Reason.DOWNLOAD_ERROR);
                }
                List<WeakReference<FetchObserver<Download>>> downloadObserverSet = downloadsObserverMap.get(download.getId());
                if (downloadObserverSet != null) {
                    downloadObserverSet.forEach(new Consumer<WeakReference<FetchObserver<Download>>>() {
                        @Override
                        public void accept(WeakReference<FetchObserver<Download>> fetchObserverWeakReference) {
                            FetchObserver<Download> observer = fetchObserverWeakReference.get();
                            if (observer != null) {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        observer.onChanged(download, Reason.DOWNLOAD_ERROR);
                                    }
                                });
                            }
                        }
                    });
                }
            }
        }

        @Override
        public void onDownloadBlockUpdated(Download download, DownloadBlock downloadBlock, int totalBlocks) {
            Timber.d("onDownloadBlockUpdated...");
            synchronized (lock) {
                fetchListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchListener>>>() {
                    @Override
                    public void accept(ArrayList<WeakReference<FetchListener>> weakReferences) {
                        Iterator<WeakReference<FetchListener>> iterator = weakReferences.iterator();
                        while (iterator.hasNext()) {
                            FetchListener fetchListener = iterator.next().get();
                            if (fetchListener == null) {
                                iterator.remove();
                            } else {
                                fetchListener.onDownloadBlockUpdated(download, downloadBlock, totalBlocks);
                            }
                        }
                    }
                });
                if (fetchGroupListenerMap != null && !fetchGroupListenerMap.isEmpty()) {
                    int groupId = download.getGroup();
                    FetchGroup fetchGroup = groupInfoProvider.getGroupReplace(groupId, (DownloadInfo) download, Reason.DOWNLOAD_BLOCK_UPDATED);
                    fetchGroupListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchGroupListener>>>() {
                        @Override
                        public void accept(ArrayList<WeakReference<FetchGroupListener>> weakReferences) {
                            Iterator<WeakReference<FetchGroupListener>> iterator = weakReferences.iterator();
                            while (iterator.hasNext()) {
                                FetchGroupListener fetchListener = iterator.next().get();
                                if (fetchListener == null) {
                                    iterator.remove();
                                } else {
                                    fetchListener.onDownloadBlockUpdated(groupId, download, downloadBlock, totalBlocks, fetchGroup);
                                }
                            }
                        }
                    });
                }
            }
        }

        @Override
        public void onStarted(Download download, List<DownloadBlock> downloadBlocks, int totalBlocks) {
            Timber.d("onStarted...");
            synchronized (lock) {
                fetchNotificationHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        synchronized (lock) {
                            for (FetchNotificationManager fetchNotificationManager : fetchNotificationManagerList) {
                                if (fetchNotificationManager.postDownloadUpdate(download)) break;
                            }
                        }
                    }
                });
                fetchListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchListener>>>() {
                    @Override
                    public void accept(ArrayList<WeakReference<FetchListener>> weakReferences) {
                        Iterator<WeakReference<FetchListener>> iterator = weakReferences.iterator();
                        while (iterator.hasNext()) {
                            FetchListener fetchListener = iterator.next().get();
                            if (fetchListener == null) {
                                iterator.remove();
                            } else {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        fetchListener.onStarted(download, downloadBlocks, totalBlocks);
                                    }
                                });
                            }
                        }
                    }
                });
                if (fetchGroupListenerMap != null && !fetchGroupListenerMap.isEmpty()) {
                    int groupId = download.getGroup();
                    FetchGroup fetchGroup = groupInfoProvider.getGroupReplace(groupId, (DownloadInfo) download, Reason.DOWNLOAD_STARTED);
                    fetchGroupListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchGroupListener>>>() {
                        @Override
                        public void accept(ArrayList<WeakReference<FetchGroupListener>> weakReferences) {
                            Iterator<WeakReference<FetchGroupListener>> iterator = weakReferences.iterator();
                            while (iterator.hasNext()) {
                                FetchGroupListener fetchListener = iterator.next().get();
                                if (fetchListener == null) {
                                    iterator.remove();
                                } else {
                                    fetchListener.onStarted(groupId, download, downloadBlocks, totalBlocks, fetchGroup);
                                }
                            }
                        }
                    });
                } else {
                    groupInfoProvider.postGroupReplace(download.getGroup(), (DownloadInfo) download, Reason.DOWNLOAD_STARTED);
                }
                List<WeakReference<FetchObserver<Download>>> downloadObserverSet = downloadsObserverMap.get(download.getId());
                if (downloadObserverSet != null) {
                    downloadObserverSet.forEach(new Consumer<WeakReference<FetchObserver<Download>>>() {
                        @Override
                        public void accept(WeakReference<FetchObserver<Download>> fetchObserverWeakReference) {
                            FetchObserver<Download> observer = fetchObserverWeakReference.get();
                            if (observer != null) {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        observer.onChanged(download, Reason.DOWNLOAD_STARTED);
                                    }
                                });
                            }
                        }
                    });
                }
            }
        }

        @Override
        public void onProgress(Download download, long etaInMilliSeconds, long downloadedBytesPerSecond) {
            Timber.d("onProgress..." + download.getProgress());
            synchronized (lock) {
                fetchNotificationHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        synchronized (lock) {
                            for (FetchNotificationManager fetchNotificationManager : fetchNotificationManagerList) {
                                if (fetchNotificationManager.postDownloadUpdate(download)) break;
                            }
                        }
                    }
                });
                fetchListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchListener>>>() {
                    @Override
                    public void accept(ArrayList<WeakReference<FetchListener>> weakReferences) {
                        Iterator<WeakReference<FetchListener>> iterator = weakReferences.iterator();
                        while (iterator.hasNext()) {
                            FetchListener fetchListener = iterator.next().get();
                            if (fetchListener == null) {
                                iterator.remove();
                            } else {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        fetchListener.onProgress(download, etaInMilliSeconds, downloadedBytesPerSecond);
                                    }
                                });
                            }
                        }
                    }
                });
                if (fetchGroupListenerMap != null && !fetchGroupListenerMap.isEmpty()) {
                    int groupId = download.getGroup();
                    FetchGroup fetchGroup = groupInfoProvider.getGroupReplace(groupId, (DownloadInfo) download, Reason.DOWNLOAD_PROGRESS_CHANGED);
                    fetchGroupListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchGroupListener>>>() {
                        @Override
                        public void accept(ArrayList<WeakReference<FetchGroupListener>> weakReferences) {
                            Iterator<WeakReference<FetchGroupListener>> iterator = weakReferences.iterator();
                            while (iterator.hasNext()) {
                                FetchGroupListener fetchListener = iterator.next().get();
                                if (fetchListener == null) {
                                    iterator.remove();
                                } else {
                                    fetchListener.onProgress(groupId, download, etaInMilliSeconds, downloadedBytesPerSecond, fetchGroup);
                                }
                            }
                        }
                    });
                } else {
                    groupInfoProvider.postGroupReplace(download.getGroup(), (DownloadInfo) download, Reason.DOWNLOAD_PROGRESS_CHANGED);
                }
                List<WeakReference<FetchObserver<Download>>> downloadObserverSet = downloadsObserverMap.get(download.getId());
                if (downloadObserverSet != null) {
                    downloadObserverSet.forEach(new Consumer<WeakReference<FetchObserver<Download>>>() {
                        @Override
                        public void accept(WeakReference<FetchObserver<Download>> fetchObserverWeakReference) {
                            FetchObserver<Download> observer = fetchObserverWeakReference.get();
                            if (observer != null) {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        observer.onChanged(download, Reason.DOWNLOAD_PROGRESS_CHANGED);
                                    }
                                });
                            }
                        }
                    });
                }
            }
        }

        @Override
        public void onPaused(Download download) {
            Timber.d("onPaused...");
            synchronized (lock) {
                fetchNotificationHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        synchronized (lock) {
                            for (FetchNotificationManager fetchNotificationManager : fetchNotificationManagerList) {
                                if (fetchNotificationManager.postDownloadUpdate(download)) break;
                            }
                        }
                    }
                });
                fetchListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchListener>>>() {
                    @Override
                    public void accept(ArrayList<WeakReference<FetchListener>> weakReferences) {
                        Iterator<WeakReference<FetchListener>> iterator = weakReferences.iterator();
                        while (iterator.hasNext()) {
                            FetchListener fetchListener = iterator.next().get();
                            if (fetchListener == null) {
                                iterator.remove();
                            } else {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        fetchListener.onPaused(download);
                                    }
                                });
                            }
                        }
                    }
                });
                if (fetchGroupListenerMap != null && !fetchGroupListenerMap.isEmpty()) {
                    int groupId = download.getGroup();
                    FetchGroup fetchGroup = groupInfoProvider.getGroupReplace(groupId, (DownloadInfo) download, Reason.DOWNLOAD_PAUSED);
                    fetchGroupListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchGroupListener>>>() {
                        @Override
                        public void accept(ArrayList<WeakReference<FetchGroupListener>> weakReferences) {
                            Iterator<WeakReference<FetchGroupListener>> iterator = weakReferences.iterator();
                            while (iterator.hasNext()) {
                                FetchGroupListener fetchListener = iterator.next().get();
                                if (fetchListener == null) {
                                    iterator.remove();
                                } else {
                                    fetchListener.onPaused(groupId, download, fetchGroup);
                                }
                            }
                        }
                    });
                } else {
                    groupInfoProvider.postGroupReplace(download.getGroup(), (DownloadInfo) download, Reason.DOWNLOAD_PAUSED);
                }
                List<WeakReference<FetchObserver<Download>>> downloadObserverSet = downloadsObserverMap.get(download.getId());
                if (downloadObserverSet != null) {
                    downloadObserverSet.forEach(new Consumer<WeakReference<FetchObserver<Download>>>() {
                        @Override
                        public void accept(WeakReference<FetchObserver<Download>> fetchObserverWeakReference) {
                            FetchObserver<Download> observer = fetchObserverWeakReference.get();
                            if (observer != null) {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        observer.onChanged(download, Reason.DOWNLOAD_PAUSED);
                                    }
                                });
                            }
                        }
                    });
                }
            }
        }

        @Override
        public void onResumed(Download download) {
            Timber.d("onResumed...");
            synchronized (lock) {
                fetchNotificationHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        synchronized (lock) {
                            for (FetchNotificationManager fetchNotificationManager : fetchNotificationManagerList) {
                                if (fetchNotificationManager.postDownloadUpdate(download)) break;
                            }
                        }
                    }
                });
                fetchListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchListener>>>() {
                    @Override
                    public void accept(ArrayList<WeakReference<FetchListener>> weakReferences) {
                        Iterator<WeakReference<FetchListener>> iterator = weakReferences.iterator();
                        while (iterator.hasNext()) {
                            FetchListener fetchListener = iterator.next().get();
                            if (fetchListener == null) {
                                iterator.remove();
                            } else {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        fetchListener.onResumed(download);
                                    }
                                });
                            }
                        }
                    }
                });
                if (fetchGroupListenerMap != null && !fetchGroupListenerMap.isEmpty()) {
                    int groupId = download.getGroup();
                    FetchGroup fetchGroup = groupInfoProvider.getGroupReplace(groupId, (DownloadInfo) download, Reason.DOWNLOAD_RESUMED);
                    fetchGroupListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchGroupListener>>>() {
                        @Override
                        public void accept(ArrayList<WeakReference<FetchGroupListener>> weakReferences) {
                            Iterator<WeakReference<FetchGroupListener>> iterator = weakReferences.iterator();
                            while (iterator.hasNext()) {
                                FetchGroupListener fetchListener = iterator.next().get();
                                if (fetchListener == null) {
                                    iterator.remove();
                                } else {
                                    fetchListener.onResumed(groupId, download, fetchGroup);
                                }
                            }
                        }
                    });
                } else {
                    groupInfoProvider.postGroupReplace(download.getGroup(), (DownloadInfo) download, Reason.DOWNLOAD_RESUMED);
                }
                List<WeakReference<FetchObserver<Download>>> downloadObserverSet = downloadsObserverMap.get(download.getId());
                if (downloadObserverSet != null) {
                    downloadObserverSet.forEach(new Consumer<WeakReference<FetchObserver<Download>>>() {
                        @Override
                        public void accept(WeakReference<FetchObserver<Download>> fetchObserverWeakReference) {
                            FetchObserver<Download> observer = fetchObserverWeakReference.get();
                            if (observer != null) {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        observer.onChanged(download, Reason.DOWNLOAD_RESUMED);
                                    }
                                });
                            }
                        }
                    });
                }
            }
        }

        @Override
        public void onCancelled(Download download) {
            Timber.d("onCancelled...");
            synchronized (lock) {
                fetchNotificationHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        synchronized (lock) {
                            for (FetchNotificationManager fetchNotificationManager : fetchNotificationManagerList) {
                                if (fetchNotificationManager.postDownloadUpdate(download)) break;
                            }
                        }
                    }
                });
                fetchListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchListener>>>() {
                    @Override
                    public void accept(ArrayList<WeakReference<FetchListener>> weakReferences) {
                        Iterator<WeakReference<FetchListener>> iterator = weakReferences.iterator();
                        while (iterator.hasNext()) {
                            FetchListener fetchListener = iterator.next().get();
                            if (fetchListener == null) {
                                iterator.remove();
                            } else {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        fetchListener.onCancelled(download);
                                    }
                                });
                            }
                        }
                    }
                });
                if (fetchGroupListenerMap != null && !fetchGroupListenerMap.isEmpty()) {
                    int groupId = download.getGroup();
                    FetchGroup fetchGroup = groupInfoProvider.getGroupReplace(groupId, (DownloadInfo) download, Reason.DOWNLOAD_CANCELLED);
                    fetchGroupListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchGroupListener>>>() {
                        @Override
                        public void accept(ArrayList<WeakReference<FetchGroupListener>> weakReferences) {
                            Iterator<WeakReference<FetchGroupListener>> iterator = weakReferences.iterator();
                            while (iterator.hasNext()) {
                                FetchGroupListener fetchListener = iterator.next().get();
                                if (fetchListener == null) {
                                    iterator.remove();
                                } else {
                                    fetchListener.onCancelled(groupId, download, fetchGroup);
                                }
                            }
                        }
                    });
                } else {
                    groupInfoProvider.postGroupReplace(download.getGroup(), (DownloadInfo) download, Reason.DOWNLOAD_CANCELLED);
                }
                List<WeakReference<FetchObserver<Download>>> downloadObserverSet = downloadsObserverMap.get(download.getId());
                if (downloadObserverSet != null) {
                    downloadObserverSet.forEach(new Consumer<WeakReference<FetchObserver<Download>>>() {
                        @Override
                        public void accept(WeakReference<FetchObserver<Download>> fetchObserverWeakReference) {
                            FetchObserver<Download> observer = fetchObserverWeakReference.get();
                            if (observer != null) {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        observer.onChanged(download, Reason.DOWNLOAD_CANCELLED);
                                    }
                                });
                            }
                        }
                    });
                }
            }
        }

        @Override
        public void onRemoved(Download download) {
            Timber.d("onRemoved...");
            synchronized (lock) {
                fetchNotificationHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        synchronized (lock) {
                            for (FetchNotificationManager fetchNotificationManager : fetchNotificationManagerList) {
                                if (fetchNotificationManager.postDownloadUpdate(download)) break;
                            }
                        }
                    }
                });
                fetchListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchListener>>>() {
                    @Override
                    public void accept(ArrayList<WeakReference<FetchListener>> weakReferences) {
                        Iterator<WeakReference<FetchListener>> iterator = weakReferences.iterator();
                        while (iterator.hasNext()) {
                            FetchListener fetchListener = iterator.next().get();
                            if (fetchListener == null) {
                                iterator.remove();
                            } else {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        fetchListener.onRemoved(download);
                                    }
                                });
                            }
                        }
                    }
                });
                if (fetchGroupListenerMap != null && !fetchGroupListenerMap.isEmpty()) {
                    int groupId = download.getGroup();
                    FetchGroup fetchGroup = groupInfoProvider.getGroupReplace(groupId, (DownloadInfo) download, Reason.DOWNLOAD_REMOVED);
                    fetchGroupListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchGroupListener>>>() {
                        @Override
                        public void accept(ArrayList<WeakReference<FetchGroupListener>> weakReferences) {
                            Iterator<WeakReference<FetchGroupListener>> iterator = weakReferences.iterator();
                            while (iterator.hasNext()) {
                                FetchGroupListener fetchListener = iterator.next().get();
                                if (fetchListener == null) {
                                    iterator.remove();
                                } else {
                                    fetchListener.onRemoved(groupId, download, fetchGroup);
                                }
                            }
                        }
                    });
                } else {
                    groupInfoProvider.postGroupReplace(download.getGroup(), (DownloadInfo) download, Reason.DOWNLOAD_REMOVED);
                }
                List<WeakReference<FetchObserver<Download>>> downloadObserverSet = downloadsObserverMap.get(download.getId());
                if (downloadObserverSet != null) {
                    downloadObserverSet.forEach(new Consumer<WeakReference<FetchObserver<Download>>>() {
                        @Override
                        public void accept(WeakReference<FetchObserver<Download>> fetchObserverWeakReference) {
                            FetchObserver<Download> observer = fetchObserverWeakReference.get();
                            if (observer != null) {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        observer.onChanged(download, Reason.DOWNLOAD_REMOVED);
                                    }
                                });
                            }
                        }
                    });
                }
            }
        }

        @Override
        public void onDeleted(Download download) {
            Timber.d("onDeleted...");
            synchronized (lock) {
                fetchNotificationHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        synchronized (lock) {
                            for (FetchNotificationManager fetchNotificationManager : fetchNotificationManagerList) {
                                if (fetchNotificationManager.postDownloadUpdate(download)) break;
                            }
                        }
                    }
                });
                fetchListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchListener>>>() {
                    @Override
                    public void accept(ArrayList<WeakReference<FetchListener>> weakReferences) {
                        Iterator<WeakReference<FetchListener>> iterator = weakReferences.iterator();
                        while (iterator.hasNext()) {
                            FetchListener fetchListener = iterator.next().get();
                            if (fetchListener == null) {
                                iterator.remove();
                            } else {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        fetchListener.onDeleted(download);
                                    }
                                });
                            }
                        }
                    }
                });
                if (fetchGroupListenerMap != null && !fetchGroupListenerMap.isEmpty()) {
                    int groupId = download.getGroup();
                    FetchGroup fetchGroup = groupInfoProvider.getGroupReplace(groupId, (DownloadInfo) download, Reason.DOWNLOAD_DELETED);
                    fetchGroupListenerMap.values().forEach(new Consumer<ArrayList<WeakReference<FetchGroupListener>>>() {
                        @Override
                        public void accept(ArrayList<WeakReference<FetchGroupListener>> weakReferences) {
                            Iterator<WeakReference<FetchGroupListener>> iterator = weakReferences.iterator();
                            while (iterator.hasNext()) {
                                FetchGroupListener fetchListener = iterator.next().get();
                                if (fetchListener == null) {
                                    iterator.remove();
                                } else {
                                    fetchListener.onDeleted(groupId, download, fetchGroup);
                                }
                            }
                        }
                    });
                } else {
                    groupInfoProvider.postGroupReplace(download.getGroup(), (DownloadInfo) download, Reason.DOWNLOAD_DELETED);
                }
                List<WeakReference<FetchObserver<Download>>> downloadObserverSet = downloadsObserverMap.get(download.getId());
                if (downloadObserverSet != null) {
                    downloadObserverSet.forEach(new Consumer<WeakReference<FetchObserver<Download>>>() {
                        @Override
                        public void accept(WeakReference<FetchObserver<Download>> fetchObserverWeakReference) {
                            FetchObserver<Download> observer = fetchObserverWeakReference.get();
                            if (observer != null) {
                                uiHandler.postTask(new Runnable() {
                                    @Override
                                    public void run() {
                                        observer.onChanged(download, Reason.DOWNLOAD_DELETED);
                                    }
                                });
                            }
                        }
                    });
                }
            }
        }
    };

    public void clearAll() {
        synchronized (lock) {
            fetchListenerMap.clear();
            fetchGroupListenerMap.clear();
            fetchNotificationManagerList.clear();
            downloadsObserverMap.clear();
        }
    }

    private List<FetchObserver<Download>> distinct(FetchObserver<Download>... fetchObservers) {
        if (fetchObservers == null) {
            return new ArrayList<>();
        }
        Set<FetchObserver<Download>> set = new LinkedHashSet<>(Arrays.asList(fetchObservers));
        return new ArrayList<>(set);
    }

    public void addFetchObserversForDownload(int downloadId, FetchObserver<Download>... fetchObservers) {
        synchronized (lock) {
            // fix 这里和安卓有差异，安卓是转Set后在toList。 Set去重的目的，这里用的contains去重。
            // Set去重是通过Download复写hasCode和equals，根据实体类的内容去比较的，而contains是通过地址对比
            List<FetchObserver<Download>> newFetchObservers = distinct(fetchObservers);
            List<WeakReference<FetchObserver<Download>>> set = downloadsObserverMap.get(downloadId) == null ? new ArrayList<>() : downloadsObserverMap.get(downloadId);
            List<FetchObserver<Download>> attachedObservers = new ArrayList<>();
            for (WeakReference<FetchObserver<Download>> info : set) {
                if (info != null && info.get() != null) {
                    attachedObservers.add(info.get());
                }
            }
            List<FetchObserver<Download>> addedObservers = new ArrayList<>();
            for (FetchObserver<Download> fetchObserver : newFetchObservers) {
                if (!attachedObservers.contains(fetchObserver)) {
                    set.add(new WeakReference<>(fetchObserver));
                    addedObservers.add(fetchObserver);
                }
            }
            Download download = downloadProvider.getDownload(downloadId);
            if (download != null) {
                uiHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        for (FetchObserver<Download> addedObserver : addedObservers) {
                            addedObserver.onChanged(download, Reason.OBSERVER_ATTACHED);
                        }
                    }
                });
            }
            downloadsObserverMap.put(downloadId, set);
        }
    }

    public void removeFetchObserversForDownload(int downloadId, FetchObserver<Download>... fetchObservers) {
        synchronized (lock) {
            for (FetchObserver<Download> fetchObserver : fetchObservers) {
                if(downloadsObserverMap.get(downloadId) != null){
                    Iterator<WeakReference<FetchObserver<Download>>> iterator = downloadsObserverMap.get(downloadId).iterator();
                    if (iterator != null) {
                        while (iterator.hasNext()) {
                            WeakReference<FetchObserver<Download>> reference = iterator.next();
                            if (reference.get() == fetchObserver) {
                                iterator.remove();
                                break;
                            }
                        }
                    }
                }
            }
        }
    }


    private class MyEventHandler extends EventHandler {
        private MyEventHandler(EventRunner runner) {
            super(runner);
        }

        // 重写实现processEvent方法
        @Override
        public void processEvent(InnerEvent event) {
            super.processEvent(event);
            if (event == null) {
                return;
            }
            int eventId = event.eventId;
            long param = event.param;

        }
    }


}
